//
//  classes.swift
//  swift_ example
//
//  Created by 魏熙美 on 2020/2/21.
//  Copyright © 2020 魏熙美. All rights reserved.
//

import Foundation

// class 类是引用类型

// 静态属性，计算属性，只读计算属性，属性观察者，属性包装，初始化器(构造函数/构造方法)
// 和 类一样，参考结构体 structs\structs.swift

// 定义类
class SomeClass {
    // 定义属性
    var width = 0
    var heigth = 0
    
    // 初始化器
    // 在实例化之前自动调用
    init() {
        
    }
    
    init(width: Int, heigth: Int) {
        self.width = width
        self.heigth = heigth
    }
    
    // 反初始化器, 只能在 class 中使用
    // 实例被垃圾回收释放之前自动调用
    deinit {
        // 实例被垃圾回收释放之前自动调用
        // 比如可以用来释放资源
        print("关闭IO资源")
    }
    
    // 静态属性
    static var nums = 10
    // 静态属性可以直接 SomeClass.nums
    
    // 定义个方法/函数， 并且有返回值
    func getWidth() -> Int {
        return self.width
    }
    
    // 实例方法
    func setWidth(width: Int) {
        self.width = width
    }
    
    // 静态函数/方法
    static func getHeigth() {
        // 静态方法不能调用实例方法 和 成员
        // 只能调用静态的
        print(self.nums)
        
        // 调用方式
        // SomeClass.getHeigth()
    }
    
    // static 和 class 修饰方法，可以互相调用
    // 都可以直接通过 类名/结构名.方法()
    
    // 使用 class关键字来允许子类重写父类对类型方法的实现
    // 也可以通静态方法方式调用
    class func printWidthAndHeigth() {
        print(self.nums)
        // 调用静态方法
        self.getHeigth()
        // 调用方式 SomeClass.printWidthAndHeigth()
    }

    
}

// 定义一个类，并继承 SomeClass
class SomeClass3: SomeClass {
    var someNum = 10
    
    // 任何属性、方法、构造器等，只要是重名了，就要加上 override 重写
    
    // 重写构造器
    override init() {
        // 调用父类构造器
        super.init()
    }
    
    init(someNum: Int) {
        // 子类的构造器，必须先调用父类的构造器
        super.init()
        self.someNum = someNum
    }
    
    override func getWidth() -> Int {
        // 调用父类方法
        // super.getWidth()
        return someNum
    }
}

// 定义类
class FixeLengRangeClass {
    // 如果定义的属性没有没有给默认值，在class里面需要给出构造器
    
    // 定义属性
    // 如果定义属性的时候没有赋值，最好指定出是什么类型
    var firstValue: Int
    // 被创建后，这个值不允许被修改
    let length: Int
    
    init() {
        self.firstValue = 0
        self.length = 0
    }
    
    init(firstValue: Int, length: Int) {
        self.firstValue = firstValue
        self.length = length
    }
}

// 定义类，定义一个计算属性(类、类和枚举都可以定义计算属性) ===================================
// 这里用类定义
class RectClass {
    var origin = SomeClass()
    
    init() {
        
    }
    
    init(origin: SomeClass) {
        self.origin = origin
    }
    
    // 定义计算属性（可以获取值 和 赋值）
    // 属性名为 center， 这个属性的返回值是 SomeClass 类型
    var center: SomeClass {
        // 获取值
        get {
            // 计算逻辑....
            
            // 最后需要返回一个 SomeClass 类型
            return SomeClass(width: origin.width * 10, heigth: origin.heigth * 10)
        }
        
        // 设置值
        // 这里的 newSomeClass 类型就是 SomeClass 类型
        // 如果没有指定 newSomeClass 这个名称，默认名称就是 newValue
        set (newSomeClass) {
            
            // 这里是赋值操作逻辑....
            
            // 最终值会赋值相应的属性
            origin.width = newSomeClass.width * 2
            origin.heigth = newSomeClass.heigth * 2
        }
    }
    
    // 只读计算属性，不能赋值
    var getSomeClassWidth: Int {
        get {
            // 读取值....
            
            return origin.width
        }
    }
    
}

// 定义类，属性观察者 ============================================
// willSet 会在该值被存储之前被调用。
// didSet 会在一个新值被存储后被调用。
class StepCounterClass {
    // 定义一个属性，并设置属性观察者
    var totalSteps: Int = 0 {
        // 这个 totalSteps 属性，在要被赋值的是调用
        // 会在该值被存储之前被调用
        // 如果没有指定 newTotaStrps 这个名称，默认名称就是 newValue
        willSet(newTotaStrps) {
            print("totalSteps 准备复制成 \(newTotaStrps)")
        }
        
        // 会在一个新值被存储后被调用
        didSet {
           // 可以使用 oldValue 获取旧值
           // 值被赋值成功后，处理一些操作
           // 比如 新旧值比对
            if totalSteps > oldValue {
                print("totalSteps属性增加了\(totalSteps - oldValue)个值")
            }
        }
    }
}

// 定义一个属性包装(类、类、枚举都可以定义包装属性) =================================
// 使用类创建包装属性
@propertyWrapper // 标记为这是一个包装属性，就可以当成注解使用 @TwelveOrLess
class TwelveOrLessClass {
    // 私有化属性
    private var number = 0
    
    // 计算属性
    // wrappedValue 属性名称是固定的，不能改变这个名称
    var wrappedValue: Int {
        get { return number }
        // 设置值的时候，保证值不会大于12
        set {
            // min 函数获取最小的那个值，第一个参数和第二个参数，谁小就返回谁
            number = min(newValue, 12)
        }
    }
    
    // Java语言称为构造器
    // 初试化器
    init() {
        // java里面是 this 关键字
        // self 关键字指的是当前的实例
        self.number = 1
    }
    
    init(wrappedValue: Int) {
        self.number = min(wrappedValue, 12)
    }
    
    
}

// 定义类，并调用上面的属性包装
class SmalRectangleClass {
    // 给类型设置上包装属性
    @TwelveOrLessClass(wrappedValue: 10) var heigth: Int
    @TwelveOrLessClass var width: Int
    
    // 设置了包装属性后，这个属性在每次赋值取值的时候，都会经过包装属性的处理逻辑
    // 这个包装属性也就是 装饰者模式
}

// 定义类，并调用属性包装，可以不使用注解形式
class SmalRectangle2Class {
    // 直接私有属性上创建 包装属性
    // 这种方式的调用包装属性，TwelveOrLess 类就可以不用加上 @propertyWrapper 注解
    // private var _heigth = TwelveOrLess(wrappedValue: 10)
    private var _heigth = TwelveOrLessClass()
    var heigth: Int {
        // 直接调取包装属性的计算属性
        get { return _heigth.wrappedValue }
        // 把新值赋值给包装属性的计算属性
        set { _heigth.wrappedValue = newValue }
    }
}


func run() {
    // 类的创建 ======================================
    
    // 创建类实例
    let someClass = SomeClass()
    print("SomeClass高度：", someClass.heigth)
    // 赋一个新值
    someClass.width = 200
    
    // 创建的时候可以成员初始化
    let someClassTh = SomeClass(width: 10, heigth: 20)
    print("SomeClassTh: ", someClassTh)
    someClassTh.heigth = 200
    
    
    // 创建时候可以初始化
    // 创建后，length 属性不允许被修改，因为是 let 类型
    let fixeLengRange = FixeLengRangeClass(firstValue: 1, length: 10)
    print("fixeLengRange: ", fixeLengRange)
    fixeLengRange.firstValue = 2
    
    
    // 调用类的计算属性 =================================
    
    let rect = RectClass(origin: SomeClass(width: 10, heigth: 20))
    print("调用计算属性之前：\(rect.origin.heigth), \(rect.origin.width)")
    
    // 给计算属性赋值 ======================================
    rect.center = SomeClass(width: 10, heigth: 20)
    print("调用计算属性后高度：", rect.center.heigth)
    print("调用计算属性后宽度：", rect.center.width)
    
    // 属性观察者 ==========================================
    let stepCounter = StepCounterClass()
    // 给这个属性赋值，会触发它的属性观察者
    stepCounter.totalSteps = 10
    
    // 包装属性 ============================================
    let smalRectangle = SmalRectangleClass()
    smalRectangle.heigth = 10
    // Prints "10"
    smalRectangle.width = 24 // 由于属性设置了包装属性，那么这个值不会超过数字 12
    // Prints "12"
    print("SmalRectangle使用了包装属性：", smalRectangle)
    
}

